/**
 * @author Alexander Sutherland (tfy11asd)
 * @date 30/05/2012
 * 
 *  Network is the class primarily in charge of running the program and 
 *  controls how many iterations or "jumps" the program has done.
 *  It makes sure all queries, agents, and nodes run
 *  their specific functions and keeps track of everything generated
 * 	by the program.
 * * 
 */

import java.util.*;


public class Network {

	public int NrOfJumps=0;
	public int MaxJumps=10000;

	private LinkedList<Agent> AgentList;
	private LinkedList<Query> QueryList;
	private LinkedList<Node> QueryNodes;
	private LinkedList<Query> ResendQuery;

	public Hashtable<Integer, Event> EventList;
	public Hashtable<Position,Node> network = new Hashtable<Position, Node>(2500,50);
	
	/**
	 * Network() constructs a sensorNetwork and assigns 4 random query nodes.
	 */

	public Network()
	{
		AgentList = new LinkedList<Agent>();
		QueryList = new LinkedList<Query>();
		ResendQuery = new LinkedList<Query>();
		QueryNodes = new LinkedList<Node>();
		EventList = new Hashtable<Integer, Event>(10, 10);
		for(int y = 0; y <50; y++)
		{
			for(int x = 0; x<50;x++)
			{
				Position p = new Position(x, y);
				Node n = new Node(p);
				network.put(p, n);
			}
		}
		// Generate 4 positions and sets query nodes from these positions
		Hashtable<Position, Position> QueryNodePos = new Hashtable<Position, Position>(8,8);
		Random rand = new Random();
		while(QueryNodePos.size()<4)
		{
			rand = new Random();
			Position p2 = new Position(rand.nextInt(49),rand.nextInt(49));
			if(QueryNodePos.get(p2)==null)
			{
				QueryNodePos.put(p2,p2);
				QueryNodes.add(network.get(p2));
			}
		}
	}
	/**
	 * iterate can be considered as a "time step" in the program, it will 
	 * make all of the queries and agents move as well as try to generate new 
	 * events from all nodes. iterate will also increase the time step by one.
	 * 
	 */

	public void iterate()
	{

		if(!AgentList.isEmpty())
		{
			int i = 0;
			while(i < AgentList.size())
			{
				((Agent) AgentList.get(i)).move();
				i++;
			}
		}
		if(!QueryList.isEmpty())
		{
			int i = 0;
			while(i < QueryList.size())
			{
				if(QueryList.get(i).nrOfJumps == 45){
					((Query) QueryList.get(i)).move();
				}
				else{
					((Query) QueryList.get(i)).move();
					i++;
				}
			}
		}
		for(int y = 0; y <50; y++)
		{
			for(int x = 0; x<50;x++)
			{
				Position p = new Position(x, y);

				((Node) network.get(p)).generateEvent(this);
			}
		}
		if(NrOfJumps%400 == 0 && !EventList.isEmpty())
		{
			for(int i = 0;i<QueryNodes.size();i++)
			{
				((Node) QueryNodes.get(i)).sendQuery(this);
			}
		}
		if(NrOfJumps%360 == 0)
		{
			while(!ResendQuery.isEmpty())
			{
				QueryList.add(ResendQuery.getFirst());
				ResendQuery.removeFirst();
			}
		}
		NrOfJumps++;
	}
	/**
	 * Adds an Event to the list of Events.
	 * @param Event e
	 **/
	public void addEvent(Event e)
	{
		EventList.put(e.id, e);
	}
	/**
	 * Adds an Agent to the list of Agents.
	 * @param Agent a
	 */
	public void addAgent(Agent a)
	{
		AgentList.add(a);
	}
	/**
	 * Adds a Query to the list of Queries.
	 * @param Query q
	 */
	public void addQuery(Query q)
	{
		QueryList.add(q);
	}
	/**
	 * Removes an Agent from the list of Agents
	 * @param Agent a
	 */
	public void removeAgent(Agent a)
	{
		AgentList.remove(a);
	}
	/**
	 * Removes a Query from the list of Queries
	 * @param Query q
	 */
	public void removeQuery(Query q)
	{
		QueryList.remove(q);
	}
	/**
	 * Adds a query to be sent again at a later time
	 * @param Query q
	 */
	public void addResend(Query q)
	{
		ResendQuery.add(q);
	}
}
